﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.Web;
using System.Web.Routing;
using SimpleWcf.Configuration;

namespace SimpleWcf
{
    /// <summary>
    /// Adds services to the RouteTable using reflection and name deduction.
    /// </summary>
    public class RoutingHttpModule : IHttpModule
    {
        /// <summary>
        /// Initializes a module and prepares it to handle requests.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application </param>
        public void Init(HttpApplication context)
        {
            //Locate all of the classes marked with a ServiceContractAttribute
            var serviceTypes = (from assembly in GetAssemblies()
                                from type in assembly.GetTypes()
                                where HasAttribute<ServiceContractAttribute>(type)
                                let alreadyExists = RouteAlreadyExists(type)
                                where alreadyExists == false
                                select NewServiceRoute(type)
                                ).ToList();

            serviceTypes.ForEach(RouteTable.Routes.Add);

            //Add the list route to list all the services.
            RouteTable.Routes.Add(new Route(ServiceBasePath+"List", new ServiceLister(serviceTypes)));
        }

        private static bool RouteAlreadyExists(Type type)
        {
            return RouteTable.Routes.Any(route => ((Route)route).Url.StartsWith(GetServiceName(type)));
        }

        private static SimpleServiceRoute NewServiceRoute(Type type)
        {
            return SimpleServiceRoute.Create(GetServiceName(type), type);
        }

        private static bool HasAttribute<T>(Type type)
        {
            return type.GetCustomAttributes(typeof(T), true).FirstOrDefault() != null
                   || type.GetInterfaces().Where(iface => iface.GetCustomAttributes(typeof(T), true).Any()).Any();
        }

        private static IEnumerable<Assembly> GetAssemblies()
        {
            if(Settings == null || Settings.Assemblies == null || Settings.Assemblies.Count == 0)
            {
                var assemblies = new List<string> { "System", "Micros", "WebDev", "CppCod", "mscorl", "SMDiag" };
                return AppDomain.CurrentDomain.GetAssemblies()
                                .Where(assemb => assemblies.Contains(assemb.FullName.Substring(0, 6)) == false);
            }
            var serviceAssemblies = new List<string>();
            for (var i = 0; i < Settings.Assemblies.Count; i++)
            {
                serviceAssemblies.Add(Settings.Assemblies[i].Name);
            }

            return AppDomain.CurrentDomain.GetAssemblies()
                            .Where(assemb => serviceAssemblies.Contains(assemb.FullName.Substring(0, assemb.FullName.IndexOf(","))));

        }

        private static string ServiceBasePath
        {
            get
            {
                var serviceBasePath = Settings.ServiceBasePath;
                if (Settings.ServiceBasePath != String.Empty && serviceBasePath.EndsWith("/") == false)
                    serviceBasePath += "/";
                return serviceBasePath;
            }
        }

        private static string GetServiceName(Type type)
        {
            var serviceExtension = (Settings.UseSvcExtension)
                                    ? ".svc"
                                    : null;

            if(type == null || type.FullName == null)
                return null;

            var serviceName = type.FullName.Substring(type.Assembly.FullName.IndexOf(",") + 1).Replace(".", "/");
            return string.Format("{0}{1}{2}", ServiceBasePath, serviceName, serviceExtension);
        }

        private static Settings Settings
        {
            get
            {
                return (Settings)ConfigurationManager.GetSection("simpleWcf") ?? new Settings();
            }
        }

        /// <summary>
        /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
        /// </summary>
        public void Dispose()
        {
        }
    }
}